"use client"

import { motion } from "framer-motion"
import type { RefObject } from "react"
import { useEffect, useId, useState } from "react"
import { ny } from "~/lib/utils"

export interface AnimatedBeamProps {
   className?: string
   containerRef: RefObject<HTMLElement> // Container ref
   fromRef: RefObject<HTMLElement>
   toRef: RefObject<HTMLElement>
   curvature?: number
   reverse?: boolean
   pathColor?: string
   pathWidth?: number
   pathOpacity?: number
   gradientStartColor?: string
   gradientStopColor?: string
   delay?: number
   duration?: number
   startXOffset?: number
   startYOffset?: number
   endXOffset?: number
   endYOffset?: number
}

export const AnimatedBeam: React.FC<AnimatedBeamProps> = ({
   className,
   containerRef,
   fromRef,
   toRef,
   curvature = 0,
   reverse = false, // Include the reverse prop
   duration = Math.random() * 3 + 4,
   delay = 0,
   pathColor = "gray",
   pathWidth = 2,
   pathOpacity = 0.2,
   gradientStartColor = "#ffaa40",
   gradientStopColor = "#9c40ff",
   startXOffset = 0,
   startYOffset = 0,
   endXOffset = 0,
   endYOffset = 0,
}) => {
   const id = useId()
   const [pathD, setPathD] = useState("")
   const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 })

   // Calculate the gradient coordinates based on the reverse prop
   const gradientCoordinates = reverse
      ? {
           x1: ["90%", "-10%"],
           x2: ["100%", "0%"],
           y1: ["0%", "0%"],
           y2: ["0%", "0%"],
        }
      : {
           x1: ["10%", "110%"],
           x2: ["0%", "100%"],
           y1: ["0%", "0%"],
           y2: ["0%", "0%"],
        }

   useEffect(() => {
      const updatePath = () => {
         if (containerRef.current && fromRef.current && toRef.current) {
            const containerRect = containerRef.current.getBoundingClientRect()
            const rectA = fromRef.current.getBoundingClientRect()
            const rectB = toRef.current.getBoundingClientRect()

            const svgWidth = containerRect.width
            const svgHeight = containerRect.height
            setSvgDimensions({ width: svgWidth, height: svgHeight })

            const startX =
               rectA.left - containerRect.left + rectA.width / 2 + startXOffset
            const startY =
               rectA.top - containerRect.top + rectA.height / 2 + startYOffset
            const endX =
               rectB.left - containerRect.left + rectB.width / 2 + endXOffset
            const endY =
               rectB.top - containerRect.top + rectB.height / 2 + endYOffset

            const controlY = startY - curvature
            const d = `M ${startX},${startY} Q ${
               (startX + endX) / 2
            },${controlY} ${endX},${endY}`
            setPathD(d)
         }
      }

      // Initialize ResizeObserver
      const resizeObserver = new ResizeObserver((entries) => {
         // For all entries, recalculate the path
         for (const entry of entries) updatePath()
      })

      // Observe the container element
      if (containerRef.current) resizeObserver.observe(containerRef.current)

      // Call the updatePath initially to set the initial path
      updatePath()

      // Clean up the observer on component unmount
      return () => {
         resizeObserver.disconnect()
      }
   }, [
      containerRef,
      fromRef,
      toRef,
      curvature,
      startXOffset,
      startYOffset,
      endXOffset,
      endYOffset,
   ])

   return (
      <svg
         fill="none"
         width={svgDimensions.width}
         height={svgDimensions.height}
         xmlns="http://www.w3.org/2000/svg"
         className={ny(
            "pointer-events-none absolute left-0 top-0 transform-gpu stroke-2",
            className,
         )}
         viewBox={`0 0 ${svgDimensions.width} ${svgDimensions.height}`}
      >
         <path
            d={pathD}
            stroke={pathColor}
            strokeWidth={pathWidth}
            strokeOpacity={pathOpacity}
            strokeLinecap="round"
         />
         <path
            d={pathD}
            strokeWidth={pathWidth}
            stroke={`url(#${id})`}
            strokeOpacity="1"
            strokeLinecap="round"
         />
         <defs>
            <motion.linearGradient
               className="transform-gpu"
               id={id}
               gradientUnits="userSpaceOnUse"
               initial={{
                  x1: "0%",
                  x2: "0%",
                  y1: "0%",
                  y2: "0%",
               }}
               animate={{
                  x1: gradientCoordinates.x1,
                  x2: gradientCoordinates.x2,
                  y1: gradientCoordinates.y1,
                  y2: gradientCoordinates.y2,
               }}
               transition={{
                  delay,
                  duration,
                  ease: [0.16, 1, 0.3, 1], // https://easings.net/#easeOutExpo
                  repeat: Number.POSITIVE_INFINITY,
                  repeatDelay: 0,
               }}
            >
               <stop stopColor={gradientStartColor} stopOpacity="0"></stop>
               <stop stopColor={gradientStartColor}></stop>
               <stop offset="32.5%" stopColor={gradientStopColor}></stop>
               <stop
                  offset="100%"
                  stopColor={gradientStopColor}
                  stopOpacity="0"
               ></stop>
            </motion.linearGradient>
         </defs>
      </svg>
   )
}
